/***  Next Do
refer : https://www.cnblogs.com/xi52qian/archive/2011/08/30/2159675.html

    "COMX:",
    "/$device/COMX",
    "/$bus/PCMCIA_0_0_0"。
 其中第一种方式只适合从0~9的设备名，亦即COM10:中10超出了设备命名规范，所以用CreateFile打开设备失败。
 使用第二种方式/$device/COM10，作为参数传递给 CreateFile，成功打开COM口，通讯正常，搞定。
 第三种是用在总线驱动上的，暂时没涉及到
***/
#include "Win32_Uart.h"
#include "basic_api.h"
#include <windows.h>

#include <TCHAR.H>

typedef HANDLE tFd;

/*** DEFINE ***/
#define UT_DRV_WIN32_UART_OC 		0
// #define WIN32_UART_DBG_OC 			0
#define WIN32_UART_RECV_DATA_OC 	0	// 串口接收数据打印

#define LLOG_ERR
#define LLOG_MARK
#define LLOG
#include "llog.h"

#ifdef	CFG_SYS_MALLOC

#define L_MALLOC 	MALLOC
#define L_FREE 		MFREE

#else
	
	#ifdef	CFG_STATIC_BUF_ALLOCATE

	#define L_MALLOC 	SMalloc
	#define L_FREE(A) 	do{LLOG_ERR(); while(1); }while(0);

	#else 

	#include <stdlib.h>
	#define L_MALLOC(A) malloc(A)
	#define L_FREE(A)	free(A)

	#endif

#endif

// 
#define FILE_POS_END 0xFFFFFFFF

/*** STRUCT ***/
struct CC_Dev_Win32_Uart{
	tFd 		hCom;		// 文件句柄
	uint8_t 	OpenFlag;		// Open Flag 
	uint32_t 	BaudRate ;
};

// typedef void* HANDLE_
// typedef HANDLE tFd;
/*** Global Val ***/

/*** Local Function ***/

//====================
#ifdef CFG_WIN32_UART		/*** 简单的 Win32 串口驱动 */

static int serial_open(tDevUart* pObj,uint8_t COM_Num, int BaudRate){
	// （WCHAR *）_ T（* filename）
	// fileapi.h（At Windows.h）
	int ret = RET_ERR;
	int len;
	uint8_t strCom[20];
	
	if(pObj == NULL )		{ LLOG_ERR(); return ret; }
	if(pObj->OpenFlag != 0)		{ LLOG_ERR(); return ret; }

	len = snprintf(strCom,sizeof(strCom),"\\\\.\\COM%d",COM_Num);
	strCom[len]=0;	
	
	// Note: 无A CreateFile 使用Unicode字符集
	pObj->hCom = CreateFileA( strCom, 					// 使用ASCII 字符集 
						GENERIC_READ | GENERIC_WRITE, 	// 允许读和写    
						0, 								// 独占方式    
						NULL,
						OPEN_EXISTING, 					// 打开而不是创建     
						0, 								// 同步方式设置为0
						NULL);// FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,//  重叠方式
	
	if(pObj->hCom == INVALID_HANDLE_VALUE){
		LLOG("OPEN uart ERR!\n");
		return ret;
	}
	
	SetupComm(pObj->hCom, 1024, 1024); //输入缓冲区和输出缓冲区的大小都是1024 

	// 设置Uart 参数
	do{
		DCB dcb;
		GetCommState(pObj->hCom, &dcb);		// 获取默认参数，然后再修改关键参数
		dcb.BaudRate 	= BaudRate;  		// 设置波特率为BaudRate
		dcb.ByteSize	= 8;     			// 每个字节有8位 
		dcb.Parity		= NOPARITY;   		// 无奇偶校验位 
		dcb.StopBits	= 1;				// ONESTOPBIT;  //一个停止位
		// dcb.fBinary	= TRUE;				
		// dcb.fParity	= TRUE;				

		if(!SetCommState(pObj->hCom, &dcb)){//	写入参数到hCom
			LLOG("#E Serial set fail\n");
			// return ret; // for PC Simulate
		}
		
	}while(0);

	/*	清空缓存区 
		参数:
		PURGE_TXABORT 中断所有写操作并立即返回，即使写操作还没有完成。
		PURGE_RXABORT 中断所有读操作并立即返回，即使读操作还没有完成。
		PURGE_TXCLEAR 清除输出缓冲区 
		PURGE_RXCLEAR 清除输入缓冲区  	
	*/
	PurgeComm(pObj->hCom, PURGE_TXCLEAR | PURGE_RXCLEAR);	 

	#if 0 //设定读写超时参数 
	do{
		COMMTIMEOUTS TimeOuts;
		TimeOuts.ReadIntervalTimeout 			= MAXDWORD;	// 1000; 
		TimeOuts.ReadTotalTimeoutMultiplier		= 10; 
		TimeOuts.ReadTotalTimeoutConstant		= 0; 		//设定写超时 
		TimeOuts.WriteTotalTimeoutMultiplier	= 500;
		TimeOuts.WriteTotalTimeoutConstant 		= 2000; 
		// SetCommTimeouts(pObj->hCom, &TimeOuts); 			//设置超时		
	}while(0);
	#endif

	// Done
	pObj->OpenFlag = 1;
	ret	= RET_OK;		
	return ret;
}

static int serial_read(tDevUart* pObj,uint16_t RNum , uint8_t* pRBuf){
	// Win32API函数 ReadFile 和 WriteFile 支持对串行口的读写操作。
	// Note: 在调用ReadFile和WriteFile之前，线程应该调用 ClearCommError 函数清除错误标志。

	DWORD rCount; 
	BOOL bReadStat;
	COMSTAT ComState;
	DWORD dwErrorFlags;

	rCount = 0;
	if(pObj == NULL)		{ LLOG_ERR(); return rCount; }
	if(pObj->OpenFlag == 0)		{ LLOG_ERR(); return rCount; }
	
	// 
	memset(&ComState,0,sizeof(COMSTAT));
	ClearCommError(pObj->hCom, &dwErrorFlags, &ComState);
	if(!ComState.cbInQue){
		// rCount = ComState.cbInQue;
		return rCount;
	}
	
	rCount = (RNum > ComState.cbInQue)?(ComState.cbInQue):RNum;
	bReadStat = ReadFile(pObj->hCom, pRBuf, rCount, &rCount, NULL);
	if (!bReadStat){
		rCount = 0;
		LLOG_ERR();
	}
	#if 0 // debug
	else{ 
		Log_ShowArray("#ReceByte: ",rCount,pRBuf);
	}
	#endif
	return rCount;
}

static int serial_write(tDevUart* pObj,uint16_t Len,uint8_t* pData){

	DWORD WLen;
	BOOL bWriteStat;
	COMSTAT ComStat;
	DWORD dwErrorFlags; 
	
	WLen = 0;
	if(pObj == NULL)		{ LLOG_ERR(); return WLen; }
	if(pObj->OpenFlag == 0)		{ LLOG_ERR(); return WLen; }
	
	ClearCommError(pObj->hCom, &dwErrorFlags, &ComStat); // Clear Err
	
	WLen = Len;
	bWriteStat = WriteFile(pObj->hCom, pData, WLen, &WLen, NULL);
	if (!bWriteStat){ 
		LLOG("write uart err !\n");
		WLen = 0;
		// PurgeComm(pObj->hCom, PURGE_TXABORT | PURGE_TXCLEAR);
	}
	// else{
		// LLOG("#I UartSend[%d]B\n", WLen);
		// Log_ShowArray("UartSend",WLen,pData[0]);		
	// }

	return WLen;
}

static int serial_close(tDevUart* pObj){
	
	int ret = RET_ERR;
	if(pObj == NULL)		{ LLOG_ERR(); return ret; }
	if(pObj->OpenFlag == 0)		{ LLOG_ERR(); return ret; }
	
	ret = RET_OK;
	CloseHandle(pObj->hCom);
	pObj->OpenFlag = 0;
	return ret;
}

int Serial_CheckAndListPort(uint16_t BufSz,uint8_t* pBuf){ 
/*** Node: List 有效的 Com 设备列表
	查找指定文件是否存在 ： 尝试打开已有 文件 ，存在则返回有效句柄

	Q-COM10 及以上的串口打开格式？
	https://support.microsoft.com/zh-cn/help/115831/howto-specify-serial-ports-larger-than-com9

	参数说明： 
	BufSz 查找范围 COM[1,BufSz-1], 对应有效 ComXX 数值记录 到 pBuf中;

*/
	uint8_t MaxPortNum;
    int i;
	uint16_t Cnt = 0;
    HANDLE hCom;
	uint8_t strCom[20];
	int len;

	Cnt = 0;
	if(pBuf == NULL)		{ LLOG_ERR(); return Cnt; }

	MaxPortNum = BufSz; // SERIAL_PORT_CHECK_MAXNUM;
    for(i = 1; i < MaxPortNum; i++){
		
		len = snprintf(strCom,sizeof(strCom),"\\\\.\\COM%d",i);
		strCom[len]=0;

        hCom = CreateFile(strCom,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,0);

        if (INVALID_HANDLE_VALUE != hCom) {
			pBuf[Cnt++]=i;
			CloseHandle(hCom);
        }
    }
	
	#if 0 // ForDebug
	LLOG("Now Serial Port: [%d]\n",j);
	for(i=0;i<Cnt;i++){
		LLOG("%d COM%d\n",i,pBuf[i]);
	}
	#endif
    return Cnt;
}

tDevUart* DevUart_Create(void){
	tDevUart* ptmp = NULL;
	ptmp = L_MALLOC(sizeof(tDevUart));
	if(ptmp == NULL)	{ LLOG_ERR(); return ptmp; }
	
	memset(ptmp,0,sizeof(tDevUart));
	ptmp->OpenFlag = 0;
	return ptmp;
}

#endif

#ifdef CFG_WIN32_UART		/*** Win32API 串口编程测试*/
static tDevUart g_UartDbg={0};
static tDevUart g_UartComm={0};

int Win32_Comm_Init(uint8_t COM_Num, int BaudRate){
	return serial_open(&g_UartComm,COM_Num,BaudRate);
}

int Win32_Comm_Close(void){
	return serial_close(&g_UartComm);
}

int Win32_Comm_SendPkg(uint32_t PkgLen,uint8_t* pPkg){
	return serial_write(&g_UartComm,PkgLen,pPkg);
}

int Win32_Comm_GetPkg(uint32_t PkgLen,uint8_t* pPkg){
	return serial_read(&g_UartComm,PkgLen,pPkg);
}

int Win32_Dbg_Init(uint8_t COM_Num, int BaudRate){
	return serial_open(&g_UartDbg,COM_Num,BaudRate);
}

int Win32_Dbg_Close(void){
	return serial_close(&g_UartDbg);
}

int Win32_Dbg_SendPkg(uint32_t PkgLen,uint8_t* pPkg){
	return serial_write(&g_UartDbg,PkgLen,pPkg);
}

int Win32_Dbg_GetPkg(uint32_t PkgLen,uint8_t* pPkg){
	return serial_read(&g_UartDbg,PkgLen,pPkg);
}
#endif

#if 0 // ifdef CFG_WIN32_UART // 封装接口 2
// static 
tDevUart* GetObjByID(uint8_t ID){
	tDevUart* ptmp=PUB_NULL;
	switch(ID){
		case DEV_UART_DBG:{
			ptmp=&g_UartDbg;
		}break;
		case DEV_UART_COMM:{
			ptmp=&g_UartComm;
		}break;		
	}
	
	return ptmp;
}

void DevUart_Reset(tDevUart* pObj){
	MEMSET((uint8_t*)pObj,0,sizeof(tDevUart));
}

int Win32_CharDrv_Init(uint8_t ID,uint8_t COM_Num, int BaudRate){
	return serial_open(GetObjByID(ID),COM_Num,BaudRate);
}

int Win32_CharDrv_SendPkg(uint8_t ID,uint32_t PkgLen,uint8_t* pPkg){
	
	tDevUart* ptmp;
	ptmp = GetObjByID(ID);
	if(ptmp == PUB_NULL){
		return 0;
	}
	return serial_write(ptmp,PkgLen,pPkg);
}

int Win32_CharDrv_GetPkg(uint8_t ID,uint32_t PkgLen,uint8_t* pPkg){
	tDevUart* ptmp;
	ptmp = GetObjByID(ID);
	if(ptmp == PUB_NULL){
		return 0;
	}	
	return serial_read(ptmp,PkgLen,pPkg);
}

#endif

